﻿// <copyright file="AdoNetDataSource.cs" company="Todd Aspeotis">
//  Copyright © 2011 Todd Aspeotis
// </copyright>

namespace LocalReportsEngine.Providers
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using LocalReportsEngine.ReportDefinitionElements;
    using LocalReportsEngine.ExtensionMethods;
    using System.Data;
    using System.Data.Common;

    /// <summary>
    /// TODO: Update summary.
    /// </summary>
    [DataProviderAttribute("SQL")]
    [DataProviderAttribute("ODBC")]
    [DataProviderAttribute("OLEDB")]
    public class AdoNetDataSource : IDataSource
    {
        private static Dictionary<string, string> providerMap = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);

        static AdoNetDataSource()
        {
            // TODO: Load from XML resource?
            providerMap.Add("SQL", "System.Data.SqlClient");
            providerMap.Add("ODBC", "System.Data.Odbc");
            providerMap.Add("OLEDB", "System.Data.OleDb");
        }

        private DataSourceElement dataSourceElement;

        public void Initialize(DataSourceElement dataSource)
        {
            this.dataSourceElement = dataSource;
        }

        public object ResolveDataSet(DataSetElement dataSet)
        {
            if (dataSourceElement.ConnectionProperties == null || dataSet.Query == null)
            {
                return null;
            }

            try
            {
                DbConnectionStringBuilder connectionStringBuilder = null;

                // TODO: Make the connection string part of the CreateConnection call
                using (DbConnection connection = CreateConnection(out connectionStringBuilder))
                {
                    connection.ConnectionString = ConnectionStringFromElement(connectionStringBuilder, dataSourceElement.ConnectionProperties);
                    connection.Open();

                    DbCommand command = connection.CreateCommand();
                    command.CommandText = dataSet.Query.CommandText;
                    command.CommandType = CommandTypeFromString(dataSet.Query.CommandType);
                    command.CommandTimeout = CommandTimeoutFromString(dataSet.Query.Timeout);
                    // TODO: QueryParameters

                    using (DbDataReader dataReader = command.ExecuteReader())
                    {
                        DataTable dataTable = new DataTable();
                        dataTable.Load(dataReader);
                        return dataTable;
                    }
                }
            }
            catch (Exception)
            {
                return null;
            }
        }

        private int CommandTimeoutFromString(string timeout)
        {
            if (string.IsNullOrEmpty(timeout))
            {
                return 30; // TODO: Bad! Use the default value from the command object when it's created
            }

            int result;

            if (int.TryParse(timeout, out result) == false)
            {
                return 30; // TODO: Bad! Use the default value from the command object when it's created
            }

            return result;
        }

        private static string ConnectionStringFromElement(DbConnectionStringBuilder connectionStringBuilder, ConnectionPropertiesElement connectionProperties)
        {
            bool integratedSecurity;

            connectionStringBuilder.ConnectionString = connectionProperties.ConnectString;

            if (bool.TryParse(connectionProperties.IntegratedSecurity, out integratedSecurity))
            {
                if (integratedSecurity)
                {
                    var sqlProviders = new [] { "SQLNCLI", "SQL Native Client", "SQLOLEDB", "SQL Server" };

                    if (connectionProperties.DataProvider.SafeEquals("SQL"))
                    {
                        connectionStringBuilder["Integrated Security"] = "SSPI";
                    }
                    else if (sqlProviders.Any(s => connectionProperties.ConnectString.IndexOf(s, StringComparison.InvariantCultureIgnoreCase) >= 0))
                    {
                        connectionStringBuilder["Integrated Security"] = "SSPI";
                    }
                }
            }

            return connectionStringBuilder.ToString();
        }

        private static CommandType CommandTypeFromString(string commandTypeString)
        {
            CommandType commandType;

            if (Enum.TryParse<CommandType>(commandTypeString, true, out commandType))
            {
                return commandType;
            }

            return CommandType.Text;
        }

        private DbConnection CreateConnection(out DbConnectionStringBuilder connectionStringBuilder)
        {
            string dataProvider = dataSourceElement.ConnectionProperties.DataProvider;
            string translatedProvider;

            if (providerMap.TryGetValue(dataProvider, out translatedProvider) == false)
            {
                translatedProvider = dataProvider;
            }

            try
            {
                DbProviderFactory providerFactory = DbProviderFactories.GetFactory(translatedProvider);

                if (providerFactory != null)
                {
                    connectionStringBuilder = providerFactory.CreateConnectionStringBuilder();
                    return providerFactory.CreateConnection();
                }
            }
            catch (Exception)
            {
            }

            // Most likely translatedProvider is not the InvariantName FactoryClass
            connectionStringBuilder = null;
            return null;
        }
    }
}
